import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

import javax.management.timer.Timer;

import net.phys2d.math.ROVector2f;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.Body;
import net.phys2d.raw.BodyList;
import net.phys2d.raw.World;
import net.phys2d.raw.shapes.Box;
import net.phys2d.raw.shapes.Circle;

import org.lwjgl.input.Mouse;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.particles.ParticleSystem;
import org.newdawn.slick.particles.effects.FireEmitter;



public class Main extends BasicGame {

	public Main() {
		super("Platformer v1 by trunksbomb");
		
	}

	@Override
	public void init(GameContainer gc) throws SlickException {
		initVariables(gc,true);		
	}
	
	public void initGuiElements(GameContainer gc) throws SlickException {
		
		guifont1x = new UnicodeFont("assets/fonts/cour.ttf", 15, true, false);
		guifont1x.addAsciiGlyphs();
		guifont1x.addGlyphs(400,600);
		guifont1x.getEffects().add(new ColorEffect(java.awt.Color.black));
		guifont1x.loadGlyphs();
		guifont2x = new UnicodeFont("assets/fonts/cour.ttf", 30, true, false);
		guifont2x.addAsciiGlyphs();
		guifont2x.addGlyphs(400,600);
		guifont2x.getEffects().add(new ColorEffect(java.awt.Color.white));
		guifont2x.loadGlyphs();
		guifontmm2x = new UnicodeFont("assets/fonts/georgia.ttf", 30, false, false);
		guifontmm2x.addAsciiGlyphs();
		guifontmm2x.addGlyphs(400,600);
		guifontmm2x.getEffects().add(new ColorEffect(java.awt.Color.white));
		guifontmm2x.loadGlyphs();
		guifontmm1x = new UnicodeFont("assets/fonts/georgia.ttf", 15, false, false);
		guifontmm1x.addAsciiGlyphs();
		guifontmm1x.addGlyphs(400,600);
		guifontmm1x.getEffects().add(new ColorEffect(java.awt.Color.white));
		guifontmm1x.loadGlyphs();
		guicolor = Color.white;
		guibarcolor = Color.black;
		
		int gap = 50;
		createGuiButton(gc,getCenterX(gc),getCenterY(gc),guimmstart, "startGame");
		createGuiButton(gc,getCenterX(gc),getCenterY(gc) + gap * 1,guimmd1, "setDiff1");
		createGuiButton(gc,getCenterX(gc),getCenterY(gc) + gap * 2,guimmd2, "setDiff2");
		createGuiButton(gc,getCenterX(gc),getCenterY(gc) + gap * 3,guimmd3, "setDiff3");
		createGuiButton(gc,getCenterX(gc),getCenterY(gc) + gap * 4,guimmd4, "setDiff4");
		createGuiButton(gc,getCenterX(gc),getCenterY(gc) + gap * 5,guimmquit, "quitGame");
		
		Color alpha = new Color(255,0,255);
		guibar = new Image("assets/gui/guibar.png", alpha);

		normaltile = new Image("assets/platforms/normal.png");
		icetile = new Image("assets/platforms/snow.png");
		mudtile = new Image("assets/platforms/mud.png");
		crumblytile = new Image("assets/platforms/crumbly.png");
		rotatestile = new Image("assets/platforms/rotates.png");
		rotatesfull = new Image("assets/platforms/rotatesfull.png");
		slidestile = new Image("assets/platforms/slides.png");
		playerimage = new Image("assets/player.png");
		bg1 = new Image("assets/bg/level1.png");
		bg2 = new Image("assets/bg/level2.png");
		bg3 = new Image("assets/bg/level3.png");
		bg4 = new Image("assets/bg/level4.png");
		bg5 = new Image("assets/bg/level5.png");
		bg6 = new Image("assets/bg/level6.png");
		bg7 = new Image("assets/bg/level7.png");

		curbg=bg1;
		deathwindow = new Image("assets/bg/deathwindow.png", alpha);
		gameoverwindow = new Image("assets/bg/gameoverwindow.png", alpha);
		mainmenu = new Image("assets/bg/mainmenu.png");
		obstacleboulder = new Image("assets/obstacles/boulder.png", alpha);
		collectablestar = new Image("assets/collectables/star.png", alpha);
		collectableflip = new Image("assets/collectables/flip.png", alpha);
		collectableslow = new Image("assets/collectables/slow.png", alpha);
		collectablefast = new Image("assets/collectables/fast.png", alpha);
		collectabletrpl = new Image("assets/collectables/trpl.png", alpha);
		collectablelife = new Image("assets/collectables/life.png", alpha);
		menucloud1 = new Image("assets/obstacles/cloud1.png", alpha);
		menucloud2 = new Image("assets/obstacles/cloud2.png", alpha);
		oldbgx = oldbgy = newbgx = newbgy = 0;
		leveltobg = new HashMap<Integer, Image>();
		mapLevels();

	}
	
	public static void initSounds() throws SlickException {
		jump = new Sound("assets/sound/jump.ogg");
		death = new Sound("assets/sound/death.ogg");
		pickup = new Sound("assets/sound/positive_1.ogg");
		applause = new Sound("assets/sound/applause.ogg");
		phaseout = new Sound("assets/sound/phaseout.ogg");
		phasein = new Sound("assets/sound/phasein.ogg");
		rotate = new Sound("assets/sound/rotate.ogg");
		
		ambientwind1 = new Sound("assets/sound/wind1.ogg");
		countdown = new Sound("assets/sound/countdown5.ogg");
		
		music = new Sound("assets/sound/light_shed_128_0.ogg");
		
		ambientwindplaying = false;
	}
	
	public void mapLevels() {
		leveltobg.put(1, bg1);
		leveltobg.put(2, bg2);
		leveltobg.put(3, bg3);
		leveltobg.put(4, bg4);
		leveltobg.put(5, bg5);
		leveltobg.put(6, bg6);
		leveltobg.put(7, bg7);
	}
	
	public Image getBg() {
		if (leveltobg.containsKey(level)) return leveltobg.get(level);
		return curbg;
	}
	
	public void initVariables(GameContainer gc, boolean resetdiff) throws SlickException {
		
		world = new World(new Vector2f(0.0f, 10.5f), 20);
		player = new EntityPlayer(30.0f, 40.0f, 20.0f, 30.0f, 5.0f);
		player.body.setFriction(0.1f);
		player.body.setMaxVelocity(40.0f, 40.0f);
		player.jumpheight = player.basejump = -160000.0f;
		bodylist = new BodyList();
		platforms = new ArrayList<Platform>();
		entities = new ArrayList<Entity>();
		entitiestoremove = new ArrayList<Entity>();
		platformstouched = new ArrayList<Body>();
		obstacles = new ArrayList<Entity>();
		collectables = new ArrayList<EntityCollectable>();
		guibuttons = new ArrayList<GuiButton>();
		menuclouds = new ArrayList<Entity>();
		particles = new ArrayList<Entity>();
		
		lastplatformspawned = ground;
		
//		int mode = ParticleSystem.BLEND_COMBINE;
//		Image image = new Image("particle.png");
//		particlesystem = new ParticleSystem(image);
//		fire = new FireEmitter((int)player.getX(), (int)player.getY(),0.01f);
		

//		particlesystem.addEmitter(fire);
		
		
		ground = new Platform(gc.getWidth()/2, gc.getHeight()-40, gc.getWidth(), 20, Body.INFINITE_MASS);
		ground.type = Platform.Type.normal;
		ground.id = "ground";
		ground.body.setFriction(0.1f);
		platforms.add(ground);
		
		world.add(player.body);
		world.add(ground.body);
		world.enableRestingBodyDetection(20.0f, 5.0f, 5.0f);
		
		totalplatforms = 0;
		platformsthislevel = 0;
		playerlives = 3;
		
		timeelapsed = 0.0f;
		platformspawntimer = 0.0f;
		obstaclespawntimer = 0.0f;
		collectablespawntimer = 0.0f;
		guileveluptimer = 0.0f;
		guipoweruptimer = 0.0f;
		menucloudspawntimer = 0.0f;
		
		guifontsize1 = 15;
		guifontsize2 = 30;
		guiheight = 40;
		guix = 0;
		guiy = gc.getHeight() - guiheight;
		score = 0;
		difficultylevel = (resetdiff ? 1 : difficultylevel);
		highscore = loadHighScore((int)difficultylevel);
		level = 1;
		
		pause = false;
		obstacletimeractive = false;
		collectabletimeractive = false;
		statechanged = false;
		
		guileveluptimeractive = false;
		guipoweruptimeractive = false;
		bgtransitionactive = false;
		
		lastpowerup = null;
		

		initGuiElements(gc);
		state = null;
		prevstate = null;
		statechanged = false;
		hoverbutton = null;
		selecteddiff = null;
		changeState(GameState.mainmenu);
		platformheight = 20.0f;
		platformwidth = 150.0f;
		speedincreasethreshold = 20;
		defaultspeed = 0.06f;
		speedincrement = 0.02f;
		difficultymult = (resetdiff ? 1.0f : difficultymult);
		platformspeed = defaultspeed * difficultymult;
		worldgravity = 10.5f;
		
		MAX_PLATFORMS = 10;
		MAX_PLAYERS = 4;
		MAX_OBSTACLES = 5;
		MAX_COLLECTABLES = 5;
		OBSTACLE_SPAWN_CHANCE = 5;
		STAR_POINT_BASE_VALUE = 150;
		PLATFORM_POINT_BASE_VALUE = 100;
		POWERUP_ACTIVE_TIME = 30000;
		
		COLLECTABLE_SPAWN_CHANCE = 25;
		SPAWN_C_STAR_CHANCE = 50;
		SPAWN_C_FLIP_CHANCE = 15;
		SPAWN_C_SLOW_CHANCE = 15;
		SPAWN_C_FAST_CHANCE = 5;
		SPAWN_C_LIFE_CHANCE = 5;
		SPAWN_C_TRPL_CHANCE = 10;
		
		c_flip_timer = 0.0f;
		c_slow_timer = 0.0f;
		c_fast_timer = 0.0f;
		c_trpl_timer = 0.0f;
		
		c_flip_active = false;
		c_slow_active = false;
		c_fast_active = false;
		c_trpl_active = false;
		c_flip_countdown_active = false;
		
		starpointvalue = (int)(STAR_POINT_BASE_VALUE * difficultymult);
		platformpointvalue = (int)(PLATFORM_POINT_BASE_VALUE * difficultymult);
		
		setSpawnUnders();
	}
	
	@Override
	public void render(GameContainer gc, Graphics g) throws SlickException {
		
		if (state.equals(GameState.mainmenu)) {
			g.setColor(Color.white);
			g.drawOval(Mouse.getX(), gc.getHeight()-Mouse.getY(), 10, 10);
			drawMainMenu(gc, g);
		}
		else {
//			g.setBackground(Color.white);
			g.drawImage(curbg,(int)oldbgx,(int)oldbgy);
			if (bgtransitionactive) g.drawImage(getBg(), (int)newbgx, (int)newbgy);
			drawPlatforms(gc, g);
//			drawParticles(gc,g);
			drawPlayer(gc, g);
			drawObstacles(gc, g);
			drawCollectables(gc, g);
			drawGUI(gc, g);
			drawDebug(gc, g);
		//	particlesystem.render();
		}
	}

	@Override
	public void update(GameContainer gc, int delta) throws SlickException {

		updateState(gc);
		
		Input input;
		if (state.equals(GameState.mainmenu)) {
			input = gc.getInput();
			
			if (input.isMousePressed(input.MOUSE_LEFT_BUTTON)) {
				onGuiClick(Mouse.getX(), gc.getHeight()-Mouse.getY());
			}
			
			updateMainMenu(gc, delta);
			updateGui(Mouse.getX(), gc.getHeight()-Mouse.getY());
			return;
		}
		
		int d = delta;
		incrementTimers(delta);
		updateVariables(delta);
//		particlesystem.update(delta);
		while (d >= 0) {
			if (player.body != null) bodylist = player.body.getTouching();

			input = gc.getInput();
			if (state.equals(GameState.awaitinginput) && input.isKeyPressed(input.KEY_R)) {
				changeState(GameState.ingame);
			}
			if (state.equals(GameState.gameover) && input.isKeyPressed(input.KEY_R)) {
				initVariables(gc, false);
				changeState(GameState.ingame);
			}
			if (input.isKeyDown(input.KEY_A) || input.isKeyDown(input.KEY_LEFT)) {
				player.move(-20.0f, 0.0f, gc);
			}
			
			if (input.isKeyDown(input.KEY_D) || input.isKeyDown(input.KEY_RIGHT)) {
				player.move(20.5f, 0.0f, gc);
			}
			
			if (input.isKeyDown(input.KEY_W)) {
				//player.move(0.0f, -20.5f, gc);
			}
			
			if (input.isKeyDown(input.KEY_S)) {
				//player.move(0.0f, 20.5f, gc);
			}
			if (input.isKeyPressed(input.KEY_SPACE)) {
				if (player.canJump()) {
					player.body.setForce(player.body.getForce().getX(), player.jumpheight);
					player.jump();
					jump.play(1.5f, 0.05f);
				}
			}
			if (input.isKeyPressed(input.KEY_R)){
				resetPlayer(true);
			}
			if (input.isKeyPressed(input.KEY_F1)) {
				debug = !debug;
			}
			if (input.isKeyPressed(input.KEY_P)) {
				if (state.equals(GameState.paused)) { 
					changeState(GameState.ingame);
				}
				else if (state.equals(GameState.ingame)) {
					changeState(GameState.paused);
				}
			}
			if (input.isKeyPressed(input.KEY_ESCAPE)) {
				changeState(GameState.mainmenu);
			}
			
			
			
//			updatePlayer(gc);
//			updatePlatforms(gc, delta);
			if (!state.equals(GameState.paused) && !state.equals(GameState.awaitinginput)) {
				world.step();
				updateBg(gc);
				updateEntities(gc, delta);
//				updateParticles(gc);
				spawnObstacles(gc);
				spawnCollectables(gc);
				removeEntities();
			}
			d--;
		}
		if (ground != null && platformsthislevel > 2) {
			entitiestoremove.add(ground);
		}
	}
	
	public void updateVariables(int delta) {
		int prevlevel = level;
		level = 1 + platformsthislevel/speedincreasethreshold;
		if (state.equals(GameState.ingame) && platformsthislevel > 2)
			score += ((float)delta/100)*level*difficultymult;
		
		platformspeed = defaultspeed * difficultymult + (level-1 + (c_slow_active ? -1: 0) + (c_fast_active ? 1 : 0)) * speedincrement;
		
		if (level > prevlevel) {
			guileveluptimeractive = true;
			bgtransitionactive = true;
			oldbgx = 0;
			oldbgy = 0;
			newbgx = 0;
			newbgy = -600;
			guileveluptimer = 1000;
			if (level == 6) guifont2x.getEffects().add(new ColorEffect(java.awt.Color.white));

	
			platformpointvalue = (int)(PLATFORM_POINT_BASE_VALUE * difficultymult * level);
			starpointvalue = (int)(STAR_POINT_BASE_VALUE * difficultymult * level);
		}
		
		//POWERUP MODS
		player.maxjumps = (c_trpl_active ? 3 : 2);
		world.setGravity(0.0f, (c_flip_active ? -worldgravity : worldgravity));
		player.jumpheight = (c_flip_active ? -player.basejump : player.basejump);
		if (!c_flip_countdown_active && c_flip_active && c_flip_timer <= 5000.0f) {
			c_flip_countdown_active = true;
			countdown.play(1.5f, 0.25f);
		}
		if (c_flip_countdown_active && c_flip_timer <= 0.0f) c_flip_countdown_active = false;
		//END POWERUP MODS
	}
	
	public void incrementTimers(int delta) {
		if (!pause) {
			timeelapsed+=delta;
			if (platformspawntimer > 0) platformspawntimer-=delta;
			
			if (obstaclespawntimer > 0) obstaclespawntimer-=delta;
			else obstacletimeractive = false;
			
			if (collectablespawntimer > 0) collectablespawntimer-=delta;
			else collectabletimeractive = false;
			
			if (guileveluptimer > 0) guileveluptimer-=delta;
			else guileveluptimeractive = false;
			
			if (guipoweruptimer > 0) guipoweruptimer-=delta;
			else guipoweruptimeractive = false;
			
			if (c_flip_timer > 0) c_flip_timer-=delta;
			else c_flip_active = false;
			
			if (c_slow_timer > 0) c_slow_timer-=delta;
			else c_slow_active = false;
			
			if (c_fast_timer > 0) c_fast_timer-=delta;
			else c_fast_active = false;
			
			if (c_trpl_timer > 0) c_trpl_timer-=delta;
			else c_trpl_active = false;
		}
	}
	
	public void resetPowerups() {
		c_flip_timer = 0.0f;
		c_slow_timer = 0.0f;
		c_fast_timer = 0.0f;
		c_trpl_timer = 0.0f;
		c_flip_active = false;
		c_slow_active = false;
		c_fast_active = false;
		c_trpl_active = false;
	}
	
	public void updateState(GameContainer gc) throws SlickException {
		if (!statechanged) return;
		
		clearOldState(gc);
		initNewState(gc);
		statechanged = false;
	}
	
	public void clearOldState(GameContainer gc) throws SlickException {
		if (prevstate == null) return;
		if (prevstate.equals(GameState.mainmenu)) {
			updateGuiButtonsEnabled(false);
			if (ambientwindplaying) {
				ambientwindplaying = false;
				ambientwind1.stop();
			}
		}
		else if (prevstate.equals(GameState.ingame) || prevstate.equals(GameState.gameover)) {
			if (state.equals(GameState.mainmenu)) {
				resetWorld(gc);
			}
		}
		else if (prevstate.equals(GameState.gameover)) {
			guigameoveractive = false;
		}
		else if (prevstate.equals(GameState.paused) || prevstate.equals(GameState.awaitinginput)) {
			gc.resume();
			guideathactive = false;
		}
		 
	}
	
	public void initNewState(GameContainer gc) {
		if (state.equals(GameState.mainmenu)) {
			updateGuiButtonsEnabled(true);
			music.stop();
		}
		if (state.equals(GameState.ingame)) {
			music.loop(1.0f, 0.05f);
		}
		if (state.equals(GameState.paused)) {
			gc.pause();
			music.stop();
		}
		if (state.equals(GameState.awaitinginput)) {
			gc.pause();
			guideathactive = true;
			music.stop();
		}
		if (state.equals(GameState.gameover)) {
			world.remove(player.body);
			guigameoveractive = true;
			music.stop();
			saveHighscore();
		}
		if (state.equals(GameState.quitting)) {
			gc.exit();
		}
	}
	
	public void resetWorld(GameContainer gc) throws SlickException {
		initVariables(gc, false);
	}
	
	public void onGuiClick(int mx, int my) {
		if (hoverbutton != null) {
			doAction(hoverbutton.onClick());
		}
	}
	
	public void updateBg(GameContainer gc) {
		if (!bgtransitionactive) return;
		
		oldbgy+=platformspeed;
		newbgy+=platformspeed;
		
		if (newbgy >= 0) {
			bgtransitionactive = false;
			curbg = getBg();
			oldbgx = oldbgy  = newbgx = newbgy = 0;
		}
	}
	
	public void updateGui(int mx, int my) {
		//Check hover
		if (guibuttons.size() > 0) {
			Iterator<GuiButton> iter = guibuttons.iterator();
			while (iter.hasNext()) {
				GuiButton gb = iter.next();
				
				if (gb.enabled && gb.isPointWithin(mx, my)) {
					hoverbutton = gb;
					gb.onHover();
				}
				if (gb.enabled && !gb.isPointWithin(mx, my) && gb.state.equals(GuiButton.ButtonState.hover)) {
					gb.onLeave();
				}
			}
		}
	}
	
	public void updateGuiButtonsEnabled(boolean enabled) {
		if (guibuttons.size() > 0) {
			Iterator<GuiButton> iter = guibuttons.iterator();
			while (iter.hasNext()) {
				GuiButton gb = iter.next();
				gb.enabled = enabled;
			}
		}
	}
	
	public void doAction(String action) {
		if (action.equals("setDiff1")) {
			setDifficulty(1);
		}
		else if (action.equals("setDiff2")) {
			setDifficulty(2);
		}
		else if (action.equals("setDiff3")) {
			setDifficulty(3);
		}
		else if (action.equals("setDiff4")) {
			setDifficulty(4);
		}
		else if (action.equals("startGame")) {
			changeState(GameState.ingame);
		}
		else if (action.equals("quitGame")) {
			changeState(GameState.quitting);
		}
	}
	
	public void setDifficulty(float d) {
		difficultylevel = (int)d;
		difficultymult = d/2;
		highscore = highscores[difficultylevel-1];
		if (state.equals(GameState.mainmenu)) {
			if (guibuttons.size() > 0) {
				Iterator<GuiButton> iter = guibuttons.iterator();
				while (iter.hasNext()) {
					GuiButton gb = iter.next();
					if (!gb.action.contains(String.valueOf((int)d)))
						gb.onDeactivate();
				}
			}
		}
	}
	
	public void updatePlayer(GameContainer gc) {
		player.x = player.body.getPosition().getX();
		player.y = player.body.getPosition().getY();
//		particlesystem.setPosition(player.getX()-player.width, player.getY()-player.height);
		player.loopPlayer(gc, c_flip_active);
		
		
		if (bodylist.size() > 0) {
			player.setOnPlatform(true);
			player.jumps = 0;
			if (!platformstouched.contains(bodylist.get(0))) {
				platformstouched.add(bodylist.get(0));
				platformsthislevel++;
				score+=platformpointvalue*difficultymult;
			}
//			if (bl.get(0).getUserData() instanceof Platform) {
//				if (((Platform)bl.get(0).getUserData()).type.equals(Platform.Type.crumbly)) {
//					(((Platform)bl.get(0).getUserData())).phaseinit = true;
//				}
//			}
			
//			System.out.print("Touching: ");
//			for (int i = 0; i < bl.size(); i++){
//				System.out.print(bl.get(i).getID() + ", ");
//			}
//			System.out.println();
		}
		
		if (player.playerlooped) {

			resetPlayer(true);
		}
	}

	public void resetPlayer(Boolean loselife) {
		changeState(GameState.awaitinginput);
//		score = 0;
		platformsthislevel = 0;
		level = 1;
		platformspeed = defaultspeed * difficultymult + (level-1) * speedincrement;
		player.playerlooped = false;
		resetPowerups();
		if (loselife) playerlives--;
		oldbgx = oldbgy = newbgx = newbgy = 0;
		bgtransitionactive = false;
		curbg = getBg();
		death.play(1.5f, 0.1f);
		guifont2x.getEffects().add(new ColorEffect(java.awt.Color.black));
		if (playerlives == 0){ 
			if (score > highscore) {
				highscore = score;
				applause.play(1.0f, 0.5f);
			}
			changeState(GameState.gameover);
		}
		player.onplatform = true;
		player.setPosition(lastplatformspawned.body.getPosition().getX(), 
				lastplatformspawned.body.getPosition().getY() - (c_flip_active ? -50 : 50), true);
		player.body.setRotation(0.0f);

	}
	
	public void updatePlatforms(GameContainer gc) throws SlickException {
		Random rand = new Random();
		//Spawn new platform if applicable
		if (platforms.size() < MAX_PLATFORMS && ((lastplatformspawned == null || lastplatformspawned.y >= 150))) {
			float px = rand.nextInt(gc.getWidth()-(int)platformwidth)+platformwidth/2;
			if (lastplatformspawned != null) {
				if (Math.abs(px-lastplatformspawned.body.getPosition().getX()) < 50) {
					px = rand.nextInt(gc.getWidth()-(int)platformwidth)+platformwidth/2;
				}
			}
			Platform p = new Platform(px,0,platformwidth,platformheight, 500.0f);
			if (p.type.equals(Platform.Type.crumbly)) p.phaseimage = crumblytile.copy();
			p.id = "p" + totalplatforms;
			p.body.addExcludedBody(ground.body);
			p.body.setUserData(p);
			lastplatformspawned = p;
			platforms.add(p);
			entities.add(p);
			world.add(p.body);
			
			totalplatforms++;
			platformspawntimer = 2000 - 100*(platformspeed-defaultspeed*difficultymult)/speedincrement;
		}
		
//		Iterator<Platform> iter = platforms.iterator();
//		while (iter.hasNext()) {
//			Platform p = iter.next();
//			if (!p.id.equalsIgnoreCase("ground")) {
//				p.body.setPosition(p.body.getPosition().getX(), (p.body.getPosition().getY() + 0.1f));
//				
//				if (p.type.equals(Platform.Type.rotates)) {
//					p.body.setRotation((float) Math.toRadians(Math.toDegrees(p.body.getRotation()) + 0.05f));
//				}
//				
//				p.x = p.body.getPosition().getX();
//				p.y = p.body.getPosition().getY();
//				if (p.x > gc.getWidth() || p.x+p.width < 0 ||
//						p.y > gc.getHeight() || p.y+p.height < 0) {
//					entitiestoremove.add(p);
//				}
//			}
//		}
		
		
		//Move platforms
		
		
		//Erase out-of-bounds platforms
//		removeEntities();
		
	}
	
	public void updatePlatform(GameContainer gc, Platform p, int delta) {
		if (!p.id.equalsIgnoreCase("ground")) {
			p.body.setPosition(p.body.getPosition().getX(), (p.body.getPosition().getY() + platformspeed));
			
			if (p.type.equals(Platform.Type.rotates)) {
				float rinc = p.rotatespeed;
				if (p.currotation < p.rotateincrement && p.rotaterest == 0) {
					p.body.setRotation((float) Math.toRadians(Math.toDegrees(p.body.getRotation()) + rinc));
					p.currotation += rinc;
					if (p.currotation >= p.rotateincrement) {
						p.currotation = 0;
						p.rotaterest = 1500.0f;
						p.playedsound = false;
					}
				}
				if (p.rotaterest > 0) p.rotaterest--;
				if (p.rotaterest <= 0) { 
					p.rotaterest = 0;
					System.out.println("rotating");
					if (!p.playedsound) {
						rotate.play(0.5f, 0.25f);
						p.playedsound = true;
					}
				}
			}
			else if (p.type.equals(Platform.Type.slides)) {
				float moveinc = 0.05f;
				if (p.slideleft && p.x > p.slideorigin - p.slideamount) {
					p.body.setPosition(p.body.getPosition().getX()-moveinc, p.body.getPosition().getY());
				}
				else if (!p.slideleft && p.x < p.slideorigin + p.slideamount) {
					p.body.setPosition(p.body.getPosition().getX()+moveinc, p.body.getPosition().getY());
				}
				else p.slideleft = !p.slideleft;
				
			}
			else if (p.type.equals(Platform.Type.crumbly)) {
				if (p.phaseinit && !p.phasesolid) {
					p.body.addExcludedBody(player.body);
					if (world.getBodies().contains(p.body)) world.remove(p.body);
					p.phaseetimer = 500;
					p.phaseinit = false;
					if (!p.playedsound) {
						phasein.play(1.0f,0.08f);
						p.playedsound = true;
					}
				}
				else if (p.phaseinit && p.phasesolid) {
					p.body.removeExcludedBody(player.body);
					if (!world.getBodies().contains(p.body)) world.add(p.body);
					p.phaseetimer = 2500;
					p.phaseinit = false;
				}
				if (p.phaseetimer <= 500 && !p.playedsound && p.phasesolid) {
					phaseout.play(1.0f, 0.25f);
					p.playedsound = true;
				}
				if (p.phaseetimer <= 0.0f) {
					p.phaseinit = true;
					p.phasesolid = !p.phasesolid;
					p.playedsound = false;
				}
				else p.phaseetimer--;
			}
			
			p.x = p.body.getPosition().getX();
			p.y = p.body.getPosition().getY();
		}
		

		//Move platforms
		
		
		//Erase out-of-bounds platforms

	}
	
	public void updateParticles(GameContainer gc) {
		Random rand = new Random();
		if (particles.size() < 20) {
			if (bodylist.size() > 0) {
				if (bodylist.get(0).getUserData() instanceof Platform) {
					Platform p = (Platform)bodylist.get(0).getUserData();
					
					Entity particle = new Entity(player.x,player.y+player.height/2);
					particle.color = p.getColor();
					particle.body.addExcludedBody(player.body);
					world.add(particle.body);

					particle.body.setForce(rand.nextInt(10)-5, -rand.nextInt(5));
				}
			}
		}
	}
	
	public void spawnObstacles(GameContainer gc) {
		Random rand = new Random();

		if (!obstacletimeractive) {
			if (obstaclespawntimer <= 0 && obstacles.size() < MAX_OBSTACLES && rand.nextInt(100) < OBSTACLE_SPAWN_CHANCE) {
				float x = rand.nextInt(gc.getWidth());
				while (x > lastplatformspawned.x - lastplatformspawned.width/2 && x < lastplatformspawned.x + lastplatformspawned.width/2) {
					x = rand.nextInt(gc.getWidth());
				}
				EntityBoulder b = new EntityBoulder(x, c_flip_active ? gc.getHeight()-guiheight : 0, 15.0f, 500.0f);
				b.body.addExcludedBody(ground.body);
				b.body.setMaxVelocity(40.0f, 20.0f);
				entities.add(b);
				obstacles.add(b);
				world.add(b.body);
			}
			obstaclespawntimer = 2500;
			obstacletimeractive = true;
		}
	}
	
	public void spawnCollectables(GameContainer gc) {
		Random rand = new Random();
		if (!collectabletimeractive) {
			if (collectablespawntimer <= 0 && collectables.size() < MAX_COLLECTABLES && rand.nextInt(100) < COLLECTABLE_SPAWN_CHANCE) {
				int type = rand.nextInt(100);
				if (type < c_star_under) {
					//Spawn star
					EntityStar s = new EntityStar(5.0f, 20.0f, lastplatformspawned);
					s.body.addExcludedBody(player.body);
					collectables.add(s);
					entities.add(s);
				}
				else if (type < c_flip_under) {
					//Spawn gravityflip
					EntityPowerup p = new EntityPowerup(7.5f, 20.0f, lastplatformspawned, EntityPowerup.Type.flipgravity);
					p.body.addExcludedBody(player.body);
					collectables.add(p);
					entities.add(p);
				}
				else if (type < c_slow_under) {
					//Spawn slowplatforms
					EntityPowerup p = new EntityPowerup(7.5f, 20.0f, lastplatformspawned, EntityPowerup.Type.slowplatforms);
					p.body.addExcludedBody(player.body);
					collectables.add(p);
					entities.add(p);
				}
				else if (type < c_fast_under) {
					//Spawn fastplatforms
					EntityPowerup p = new EntityPowerup(7.5f, 20.0f, lastplatformspawned, EntityPowerup.Type.fastplatforms);
					p.body.addExcludedBody(player.body);
					collectables.add(p);
					entities.add(p);
				}
				else if (type < c_life_under) {
					//Spawn extralife
					EntityPowerup p = new EntityPowerup(7.5f, 20.0f, lastplatformspawned, EntityPowerup.Type.extralife);
					p.body.addExcludedBody(player.body);
					collectables.add(p);
					entities.add(p);
				}
				else if (type < c_trpl_under) {
					//Spawn triplejump
					EntityPowerup p = new EntityPowerup(7.5f, 20.0f, lastplatformspawned, EntityPowerup.Type.triplejump);
					p.body.addExcludedBody(player.body);
					collectables.add(p);
					entities.add(p);
				}
				
			}
			collectablespawntimer = 2500;
			collectabletimeractive = true;
		}
	}
	
	public void updateObstacle(GameContainer gc, EntityBoulder b) {
		if (!b.hitplayer && b.body.getTouching().contains(player.body) && ((c_flip_active && b.y-b.width > player.y) || (!c_flip_active && b.y+b.width < player.y))) {
			death.play(1.5f, 0.1f);
			b.hitplayer = true;
		}
	}
	
	public void updateCollectable(GameContainer gc, EntityCollectable c) {

		c.setPosition(c.x, c.y += platformspeed, false);
		float distance = c.body.getPosition().distance(player.body.getPosition());
		if (distance < 18) {	
			pickup.play(1.0f, 0.3f);

			if (c instanceof EntityStar) {
				score += starpointvalue*difficultymult;
			}
			if (c instanceof EntityPowerup) {
				EntityPowerup p = (EntityPowerup)c;
				if (p.type.equals(EntityPowerup.Type.flipgravity)) {
					c_flip_timer = POWERUP_ACTIVE_TIME;
					c_flip_active = true;
					c_flip_countdown_active = false;
				}
				else if (p.type.equals(EntityPowerup.Type.slowplatforms)) {
					c_slow_timer = POWERUP_ACTIVE_TIME;
					c_slow_active = true;
				}
				else if (p.type.equals(EntityPowerup.Type.fastplatforms)) {
					c_fast_timer = POWERUP_ACTIVE_TIME;
					c_fast_active = true;
				}
				else if (p.type.equals(EntityPowerup.Type.triplejump)) {
					c_trpl_timer = POWERUP_ACTIVE_TIME;
					c_trpl_active = true;
				}
				else if (p.type.equals(EntityPowerup.Type.extralife)) {
					playerlives++;
				}
				
				lastpowerup = (EntityPowerup)c;
				guipoweruptimer = 1500.0f;
				guipoweruptimeractive = true;
			}
			entitiestoremove.add(c);
		}

	}
	
	public void updateEntities(GameContainer gc, int delta) throws SlickException {
		if (entities.size() > 0) {
			Iterator<Entity> iter = entities.iterator();
			while (iter.hasNext()) {
				Entity e = iter.next();
				e.x = e.body.getPosition().getX();
				e.y = e.body.getPosition().getY();
				
				if (e instanceof EntityPlayer) {
					updatePlayer(gc);
				}
				if (e instanceof Platform) {
					updatePlatform(gc, (Platform)e, delta);
				}
				if (e instanceof EntityBoulder) {
					updateObstacle(gc, (EntityBoulder)e);
				}
				if (e instanceof EntityCollectable) {
					updateCollectable(gc, (EntityCollectable)e);
				}
				if (e.x > gc.getWidth() || e.x+e.width < 0 ||
						e.y-e.height/2 > gc.getHeight()-guiheight || e.y+e.height < 0) {
					entitiestoremove.add(e);
				}
			}
		}
		
		updatePlatforms(gc);
		updatePlayer(gc);
	}
	
	public void updateMainMenu(GameContainer gc, int delta) {
		Random rand = new Random();
		if (menucloudspawntimer <= 0.0f && menuclouds.size() < 4) {
			
			float x = rand.nextInt(2);
			float y = rand.nextInt(150);
			Entity cloud = new Entity((x == 0 ? -100 : gc.getWidth() + 100), 50 + y);
			cloud.id = (x == 0 ? "right" : "left");
			cloud.weight = (rand.nextInt(2) == 0 ? 1 : 2);
			cloud.height = 1+rand.nextFloat();
			menuclouds.add(cloud);
			menucloudspawntimer = 5000.0f;
		}
		
		if (menuclouds.size() > 0) {
			Iterator<Entity> iter = menuclouds.iterator();
			while (iter.hasNext()) {
				Entity e = iter.next();
				if (e.id.equalsIgnoreCase("left")) {
					e.x -= 0.01f * delta * e.height;
				}
				else if (e.id.equalsIgnoreCase("right")) {
					e.x += 0.01f * delta * e.height;
				}
				else System.err.println("Something went wrong with this cloud..");
				
				if (rand.nextInt(2) == 0 && e.y+0.01*delta <= 200) {
					e.y += 0.01 * delta;
				}
				else if (e.y-0.01*delta >= 0){
					e.y -= 0.01 * delta;
				}
			}
		}
		
		if (rand.nextInt(100) < 100 && !ambientwindplaying) {
			ambientwind1.loop(1.0f, 0.05f);
			ambientwindplaying = true;
		}
		
		if (menucloudspawntimer > 0) menucloudspawntimer-=delta;
	}
	
	public void setSpawnUnders() {
		int curunder = 0;
		
		c_star_under = (curunder+=SPAWN_C_STAR_CHANCE);
		c_flip_under = (curunder+=SPAWN_C_FLIP_CHANCE);
		c_slow_under = (curunder+=SPAWN_C_SLOW_CHANCE);
		c_fast_under = (curunder+=SPAWN_C_FAST_CHANCE);
		c_life_under = (curunder+=SPAWN_C_LIFE_CHANCE);
		c_trpl_under = (curunder+=SPAWN_C_TRPL_CHANCE);		
	}
	
	public void drawDebug(GameContainer gc, Graphics g) {
		if (!debug) return;
		float xpos = 10.0f;
		float xinc = 0.0f;
		float ypos = 30.0f;
		float yinc = 20.0f;
		g.setColor(Color.red);
		g.drawString("pos: " + player.x + ", " + player.y, xpos,ypos);
		ypos+=yinc;
		g.drawString("Total Platforms: " + totalplatforms, xpos, ypos);
		ypos+=yinc;
		g.drawString("Time elapsed: " + timeelapsed/1000 + " secs", xpos, ypos);
		ypos+=yinc;
		g.drawString("Platform Speed: " + platformspeed, xpos, ypos);
		g.drawString("WASD to move, P to pause, R to reset position", 10, gc.getHeight()-guiheight-20);
	}
	
	public void drawGUI(GameContainer gc, Graphics g) {
		float lineheight;
		if (state.equals(GameState.paused)) {
			float p1width = guifont1x.getWidth(guipause1);
			float p2width = guifont1x.getWidth(guipause2);
			lineheight = guifont1x.getLineHeight();
			
//			g.drawString("PAUSED", gc.getWidth()/2, gc.getHeight()/2);
//			g.drawString("press p to resume", gc.getWidth()/2, gc.getHeight()/2 + 20);
			guifont1x.drawString((gc.getWidth()-p1width)/2, (gc.getHeight()-guiheight-lineheight)/2, guipause1, guicolor);
			guifont1x.drawString((gc.getWidth()-p2width)/2, (gc.getHeight()-guiheight-lineheight)/2+lineheight, guipause2, guicolor);
		}
		
		if (guileveluptimeractive) {
			float w1 = guifont2x.getWidth(guilevelup);
			float w2 = guifont2x.getWidth(String.valueOf(level));
			lineheight = guifont2x.getLineHeight();
			g.setColor(Color.white);
			guifont2x.drawString((gc.getWidth()-w1)/2, (gc.getHeight()-guiheight-lineheight)/2, guilevelup, guicolor);
			guifont2x.drawString((gc.getWidth()-w2)/2, (gc.getHeight()-guiheight-lineheight)/2+lineheight, String.valueOf(level), guicolor);
		}

		if (guipoweruptimeractive) {
			String s1 = lastpowerup.toString() + " active!";
			float w1 = guifont2x.getWidth(s1);
			lineheight = guifont2x.getLineHeight();
			guifont2x.drawString((gc.getWidth()-w1)/2, 10, s1, guicolor);
		}
		
		if (guideathactive) {
			float w1 = guifont2x.getWidth(guideath1);
			float w2 = guifont1x.getWidth(guideath2);
			deathwindow.drawCentered(getCenterX(gc), getCenterY(gc)+guifont2x.getLineHeight());
			guifont2x.drawString(getCenterX(gc)-w1/2, getCenterY(gc)-guifont2x.getLineHeight()/2, guideath1);
			guifont1x.drawString(getCenterX(gc)-w2/2, getCenterY(gc)-guifont1x.getLineHeight()/2+40, guideath2);
		}
		
		if (state.equals(GameState.gameover)) {
			gameoverwindow.drawCentered(getCenterX(gc), getCenterY(gc)+guifont2x.getLineHeight());
			float w1 = guifont2x.getWidth(guigameover1);
			float w2 = guifont1x.getWidth(guigameover2);
			float w3 = guifont1x.getWidth(guigameover3);
			
			guifont2x.drawString(getCenterX(gc)-w1/2, getCenterY(gc)-guifont2x.getLineHeight()/2, guigameover1);
			guifont1x.drawString(getCenterX(gc)-w2/2, getCenterY(gc)-guifont1x.getLineHeight()/2+40, guigameover2);
			guifont1x.drawString(getCenterX(gc)-w3/2, getCenterY(gc)-guifont1x.getLineHeight()/2+60, guigameover3);
		}
		
		if (state.equals(GameState.ingame) || state.equals(GameState.paused) || state.equals(GameState.awaitinginput) || state.equals(GameState.gameover)) {
			g.drawImage(guibar, 0.0f, gc.getHeight()-guiheight);

			String s1 = String.valueOf((int)score);
			String s2 = String.valueOf((int)highscore);
			String s3 = String.valueOf(playerlives);
			String s4 = String.valueOf(level);
			
			String p1 = String.valueOf((int)c_flip_timer/1000);
			String p2 = String.valueOf((int)c_slow_timer/1000);
			String p3 = String.valueOf((int)c_fast_timer/1000);
			String p4 = String.valueOf((int)c_trpl_timer/1000);
			
			float s1width = guifont1x.getWidth(s1);
			float s2width = guifont1x.getWidth(s2);
			float s3width = guifont1x.getWidth(s3);
			float s4width = guifont1x.getWidth(s4);
			
			float p1width = guifont1x.getWidth(p1);
			float p2width = guifont1x.getWidth(p2);
			float p3width = guifont1x.getWidth(p3);
			float p4width = guifont1x.getWidth(p4);
			
			lineheight = guifont1x.getLineHeight();
			guifont1x.drawString(guix+30, guiy+3, "SCORE", guibarcolor);
			guifont1x.drawString(guix+145, guiy+3, "HI-SCORE", guibarcolor);
			guifont1x.drawString(guix+245, guiy+3, "LIVES", guibarcolor);
			guifont1x.drawString(guix+300, guiy+3, "LVL", guibarcolor);

			guifont1x.drawString(guix+343, guiy+3, "GVTY", guibarcolor);
			guifont1x.drawString(guix+388, guiy+3, "SLOW", guibarcolor);
			guifont1x.drawString(guix+433, guiy+3, "FAST", guibarcolor);
			guifont1x.drawString(guix+478, guiy+3, "JUMP", guibarcolor);

			guifont1x.drawString(guix+70-s1width/2, guiy+15, s1, guibarcolor);
			guifont1x.drawString(guix+190-s2width/2, guiy+15, s2, guibarcolor);
			guifont1x.drawString(guix+270-s3width/2, guiy+15, s3, guibarcolor);
			guifont1x.drawString(guix+315-s4width/2, guiy+15, s4, guibarcolor);

			//Draw active powerups
			guifont1x.drawString(guix+360-p2width/2, guiy+15, p1, guibarcolor);
			guifont1x.drawString(guix+407-p3width/2, guiy+15, p2, guibarcolor);
			guifont1x.drawString(guix+454-p4width/2, guiy+15, p3, guibarcolor);
			guifont1x.drawString(guix+501-p4width/2, guiy+15, p4, guibarcolor);

			
		}
	}
	
	public void drawParticles(GameContainer gc, Graphics g) {
		if (particles.size() > 0) {
			Iterator iter = particles.iterator();
			while (iter.hasNext()) {
				Entity p = (Entity) iter.next();
				
				g.fillRect(p.x, p.y, 4, 3);
			}
		}
	}
	
	public void drawTiles(GameContainer gc, Graphics g, Platform p) {
		Image image = normaltile;
		
		if (p.type.equals(Platform.Type.normal)) {
			image = normaltile;
		}
		else if (p.type.equals(Platform.Type.ice)){
			image = icetile;
		}
		else if (p.type.equals(Platform.Type.mud)){
			image = mudtile;
		}
		else if (p.type.equals(Platform.Type.crumbly)){
			image = crumblytile;
		}
		else if (p.type.equals(Platform.Type.rotates)){
			image = rotatestile;
			rotatesfull.setRotation(p.currotation);
			rotatesfull.drawCentered((int)p.x, (int)p.y);
			return;
		}
		else if (p.type.equals(Platform.Type.slides)){
			image = slidestile;
		}
		else image = normaltile;
		
		for (int i=0; i<p.width/10; i++) {
			drawTile(g,image,p.x-p.width/2+10*i, p.y-p.height/2, p.currotation,p);
		}
	}
	
	public void drawTile(Graphics g, Image image, float x, float y, float angle, Platform p) {
//		g.drawImage(image, x, y);
//		image.setRotation(angle);
		if (angle != 0) {
			g.rotate(x + p.x, y + p.y, angle);
			g.drawImage(image, (int)x, (int)y);

	        g.resetTransform();
		}
		
		if (p.type.equals(Platform.Type.crumbly)) {
			if (p.phasesolid) {
				p.phaseimage.setAlpha(0.1f);
				if (p.phaseetimer < 500) {
					p.phaseimage.setAlpha(p.phaseetimer/500);
				}					
				else if (p.phaseetimer <= 2500) {
					p.phaseimage.setAlpha(1.0f);
				}
			
			}
			p.phaseimage.draw((int)x,(int)y);
		}
		else
			image.draw((int)x, (int)y);
	}
	
	
	public void drawPlatforms(GameContainer gc, Graphics g) throws SlickException {
		if (platforms.size() == 0)
			return;
		
		Iterator<Platform> iter = platforms.iterator();
		while (iter.hasNext()) {
			Platform p = iter.next();
			Color old = g.getColor();
			g.setColor(new Color (0,0,0,(float) 0.5));
			if (!p.type.equals(Platform.Type.rotates))
				g.fillRect(((int)p.x-p.width/2+5), ((int)p.y+-p.height/2+5), p.width, p.height);
			g.setColor(old);
			drawTiles(gc,g,p);

//			g.drawRect(p.body.getPosition().getX(), p.body.getPosition().getY(), p.body.getShape().getBounds().getWidth(), p.body.getShape().getBounds().getHeight());
//			g.drawRect(p.x, p.y, p.width, p.height);
			
			if(debug) {
				drawRectangle(g, p.getColor(), p.body);
				if (p.type.equals(Platform.Type.crumbly) && p.phasesolid) {
					g.drawString(String.valueOf(p.phaseetimer), p.x, p.y);
				}
				else if (p.type.equals(Platform.Type.rotates) && p.rotaterest > 0) {
					g.drawString(String.valueOf(p.rotaterest), p.x, p.y);
				}
				else if (p.type.equals(Platform.Type.ice)) {
	//				g.drawImage(new Image("assets/platforms/snow_full.png"), p.x-p.width/2, p.y-p.height/2, null);
				}
				else g.drawString(p.type.toString(), p.x, p.y);
			}
		}
	}
	
	public void drawPlayer(GameContainer gc, Graphics g) {
		if (state.equals(GameState.gameover)) return;
		playerimage.setRotation((float) Math.toDegrees(player.body.getRotation()));
		playerimage.draw(((int)player.x-player.width/2),((int)player.y-player.height/2));
		if (debug)
			drawRectangle(g, Color.red, player.body);
	}
	
	public void drawObstacles(GameContainer gc, Graphics g) {
		if (obstacles.size() > 0) {
			Iterator<Entity> iter = obstacles.iterator();
			while (iter.hasNext()) {
				Entity o = iter.next();
				
				if (o instanceof EntityBoulder) {
					obstacleboulder.setRotation((float) Math.toDegrees(o.body.getRotation()));
					obstacleboulder.drawCentered((int)o.x, (int)o.y);
					if (debug) drawCircle(g, Color.gray, o, (int)o.width);
				}
			}
		}
	}
	
	public void drawCollectables(GameContainer gc, Graphics g) {
		if (collectables.size() > 0) {
			Iterator<EntityCollectable> iter = collectables.iterator();
			while (iter.hasNext()) {
				EntityCollectable o = iter.next();
				
				if (o instanceof EntityStar) {
					collectablestar.drawCentered((int)o.x, (int)o.y);
				}
				if (o instanceof EntityPowerup) {
					EntityPowerup p = (EntityPowerup)o;
					
					if (p.type.equals(EntityPowerup.Type.flipgravity))
						collectableflip.drawCentered((int)p.x, (int)p.y);
					else if (p.type.equals(EntityPowerup.Type.slowplatforms))
						collectableslow.drawCentered((int)p.x, (int)p.y);
					else if (p.type.equals(EntityPowerup.Type.fastplatforms))
						collectablefast.drawCentered((int)p.x, (int)p.y);
					else if (p.type.equals(EntityPowerup.Type.triplejump))
						collectabletrpl.drawCentered((int)p.x, (int)p.y);
					else if (p.type.equals(EntityPowerup.Type.extralife))
						collectablelife.drawCentered((int)p.x, (int)p.y);
				}
				
				if (debug)drawCircle(g, o.getColor(), o, (int)o.width);
			}
		}
	}
	
	public void drawCircle(Graphics g, Color color, Entity e, int radius) {
		drawCircleBody(g, e.body, (Circle)e.body.getShape(), true, color, Color.white);
	}
	
	  protected void drawCircleBody(Graphics g, Body body, Circle circle, boolean fill, Color fillcolor, Color linecolor) {
		    float x = body.getPosition().getX();
		    float y = body.getPosition().getY();
		    float r = circle.getRadius();
		    float rot = body.getRotation();
		    float xo = (float) (Math.cos(rot) * r);
		    float yo = (float) (Math.sin(rot) * r);
		    
		    
		    if (fill) {
		    	g.setColor(fillcolor);
		    	g.fillOval((int) (x-r),(int) (y-r),(int) (r*2),(int) (r*2));
		    }
		    g.setColor(linecolor);
		    g.drawOval((int) (x-r),(int) (y-r),(int) (r*2),(int) (r*2));
		    g.setColor(Color.orange);
		    g.drawLine((int) x,(int) y,(int) (x+xo),(int) (y+yo));
		  }
	
	public void drawRectangle(Graphics g, Color color, Body body) {
        Vector2f[] pts = ((Box)body.getShape()).getPoints(body.getPosition(), body.getRotation());
        
        Vector2f v1 = pts[0];
        Vector2f v2 = pts[1];
        Vector2f v3 = pts[2];
        Vector2f v4 = pts[3];
        
//        g.setColor(color);
//        g.fillRect(v1.x, v1.y, v2.x-v1.x, v4.y-v1.y);
//        
//        g.drawString("v1", v1.x, v1.y);
//        g.drawString("v2", v2.x, v2.y);
//        g.drawString("v3", v3.x, v3.y);
//        g.drawString("v4", v4.x, v4.y);
        g.setColor(color);
        g.drawLine((int) v1.x,(int) v1.y,(int) v2.x,(int) v2.y);
        g.drawLine((int) v2.x,(int) v2.y,(int) v3.x,(int) v3.y);
        g.drawLine((int) v3.x,(int) v3.y,(int) v4.x,(int) v4.y);
        g.drawLine((int) v4.x,(int) v4.y,(int) v1.x,(int) v1.y);
        
	}
	
	public void drawMainMenu(GameContainer gc, Graphics g) {
		float lineheight = guifontmm2x.getLineHeight();
		float linegap = 20;
		String d = guimmd1 + "  " + guimmd2 + "  " + guimmd3 + "  " + guimmd4;
		
		mainmenu.draw(0,0);
//		g.setBackground(Color.blue);
		guifontmm2x.drawString(getCenterX(gc)-guifontmm2x.getWidth(guimmtitle)/2, 200, guimmtitle);
		guifontmm1x.drawString(136-guifontmm1x.getWidth(guimmcontrols)/2, 525, guimmcontrols);
		guifontmm1x.drawString(136-guifontmm1x.getWidth(guimmcontrols2)/2, 525+guifontmm1x.getLineHeight(), guimmcontrols2);

//		guifontmm.drawString(getCenterX(gc)-guifontmm.getWidth(d)/2, getCenterY(gc) + lineheight * 2 + linegap, d);
//		guifontmm.drawString(getCenterX(gc)-guifontmm.getWidth(guimmquit)/2, getCenterY(gc) + lineheight * 3 + linegap * 2, guimmquit);
		
		if (guibuttons.size() > 0) {
			Iterator<GuiButton> iter = guibuttons.iterator();
			
			while (iter.hasNext()) {
				GuiButton gb = iter.next();
				drawGuiButton(g,gb,true);
			}
		}
		
		if (menuclouds.size() > 0) {
			Iterator<Entity> iter = menuclouds.iterator();
			while (iter.hasNext()) {
				Entity c = iter.next();
				if (c.weight == 1) {
					menucloud1.drawCentered((int)c.x, (int)c.y);
				}
				else menucloud2.drawCentered((int)c.x, (int)c.y);
				
			}
		}
	}
	
	public void createGuiButton(GameContainer gc, int posX, int posY, String text, String action) {
		int paddingx = 40;
		int paddingy = 10;
		int width = guifontmm1x.getWidth(text) + paddingx*2;
		int height = guifontmm1x.getLineHeight() + paddingy*2;
		GuiButton gb = new GuiButton(posX-width/2,posY-height/2,width,height, text, action);
		
		guibuttons.add(gb);
	}
	
	public void drawGuiButton(Graphics g, GuiButton gb, boolean usemaxwidth) {
		g.setColor(gb.color);
		g.drawRect((usemaxwidth ? gb.maxbuttonx : gb.x), gb.y, (usemaxwidth ? gb.maxbuttonwidth : gb.width), gb.height);
		guifontmm1x.drawString(gb.x + gb.paddingx , gb.y + gb.paddingy, gb.text);
	}
	
	public int getCenterX(GameContainer gc) {
		return gc.getWidth()/2;
	}
	
	public int getCenterY(GameContainer gc) {
		return gc.getHeight()/2;
	}
	
	public void changeState(GameState newstate) {
		statechanged = true;
		prevstate = state;
		state = newstate;
	}
	
	public void saveHighscore() {
		highscores[difficultylevel-1] = (int) highscore;
		try{
			  // Create file 
			  FileWriter fstream = new FileWriter("config.ini");
			  BufferedWriter out = new BufferedWriter(fstream);
			  for (int i=0;i<highscores.length;i++) {
				  out.write("hiscore" + difficultylevel + "=" + highscores[i]);
				  out.newLine();
			  }
			  //Close the output stream
			  out.close();
			  }catch (Exception e){//Catch exception if any
			  System.err.println("Error: " + e.getMessage());
			  }
	}
	
	public int loadHighScore(int diff) {
		int hs = 0;
		highscores = new int[4];
		try{
			FileInputStream fstream = new FileInputStream("config.ini");
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			//Read File Line By Line
			int i = 0;
			while ((strLine = br.readLine()) != null)   {
				highscores[i] = parseVariable(strLine);
				System.out.println("Loaded hi for " + (i+1) + " as " + highscores[i]);
				i++;
			}
			//Close the input stream
			in.close();
		}catch (Exception e){//Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
		System.out.println("hi for " + diff + " is " + highscores[diff-1]);
		return highscores[diff-1];
	}
	
	public int parseVariable(String strLine) {
		System.out.println("high score is " + strLine);
		return Integer.parseInt(strLine.substring(strLine.indexOf('=')+1));
	}
	public void removeEntities() {
		if (entitiestoremove.size() > 0) {
			Iterator<Entity> iter = entitiestoremove.iterator();
			while (iter.hasNext()) {
				Entity e = iter.next();
				entities.remove(e);
				world.remove(e.body);
				if (e instanceof Platform) {
					platforms.remove(e);
					if (platformstouched.contains(e.body)) {
						platformstouched.remove(e.body);
					}
				}
				if (e instanceof EntityBoulder) {
					obstacles.remove(e);
				}
				if (e instanceof EntityCollectable) {
					collectables.remove(e);
					entities.remove(e);
				}
				e = null;
			}
			entitiestoremove.clear();
		}
	}
	
	public static void main(String[] args) throws SlickException {
		AppGameContainer app = new AppGameContainer(new Main());
		app.setDisplayMode(800, 600, false);
		app.setTargetFrameRate(60);
		app.setVSync(true);
		initSounds();
		app.start();
		
	}
	
	public World world;
	public EntityPlayer player;
	public Platform ground;
	public BodyList bodylist;
	public ArrayList<Platform> platforms;
	public ArrayList<Entity> entities;
	public ArrayList<Entity> obstacles;
	public ArrayList<Entity> entitiestoremove;
	public ArrayList<Body> platformstouched;
	public ArrayList<EntityCollectable> collectables;
	public ArrayList<GuiButton> guibuttons;
	public ArrayList<Entity> menuclouds;
	public ArrayList<Entity> particles;
	
	public FireEmitter fire;
	public ParticleSystem particlesystem;

	
	public Platform lastplatformspawned;
	
	public float totalplatforms;
	public float timeelapsed;
	public float obstaclespawntimer;
	public float platformspawntimer;
	public float collectablespawntimer;
	public float c_flip_timer;
	public float c_slow_timer;
	public float c_fast_timer;
	public float c_trpl_timer;
	
	public float score;
	public float highscore;
	
	public float platformwidth;
	public float platformheight;
	public float speedincrement;
	public float defaultspeed;
	public float difficultymult;
	public float platformspeed;
	public float worldgravity;
	
	public float guileveluptimer;
	public float guipoweruptimer;
	public float menucloudspawntimer;
	
	public float oldbgx;
	public float oldbgy;
	public float newbgx;
	public float newbgy;

	public int level;
	public int platformsthislevel;
	public int speedincreasethreshold;
	public int starpointvalue;
	public int platformpointvalue;
	public int difficultylevel;
	public int[] highscores;
	
	public int playerlives;

	public int MAX_PLATFORMS;
	public int MAX_PLAYERS;
	public int MAX_OBSTACLES;
	public int MAX_COLLECTABLES;
	public int OBSTACLE_SPAWN_CHANCE;
	public int STAR_POINT_BASE_VALUE;
	public int PLATFORM_POINT_BASE_VALUE;
	public int POWERUP_ACTIVE_TIME;
	
	public int COLLECTABLE_SPAWN_CHANCE;
	public int SPAWN_C_STAR_CHANCE;
	public int SPAWN_C_FLIP_CHANCE;
	public int SPAWN_C_SLOW_CHANCE;
	public int SPAWN_C_FAST_CHANCE;
	public int SPAWN_C_LIFE_CHANCE;
	public int SPAWN_C_TRPL_CHANCE;
	
	public int c_star_under;
	public int c_flip_under;
	public int c_slow_under;
	public int c_fast_under;
	public int c_life_under;
	public int c_trpl_under;
	
	public int guifontsize1;
	public int guifontsize2;
	public int guiheight;
	public int guix;
	public int guiy;

	public boolean pause;
	public boolean debug;
	public boolean statechanged;
	
	public boolean obstacletimeractive;
	public boolean collectabletimeractive;
	public boolean c_flip_timer_active;
	public boolean c_slow_timer_active;
	public boolean c_fast_timer_active;
	public boolean c_trpl_timer_active;
	
	public boolean c_flip_active;
	public boolean c_slow_active;
	public boolean c_fast_active;
	public boolean c_trpl_active;
	public boolean c_flip_countdown_active;
	
	public boolean guileveluptimeractive;
	public boolean guipoweruptimeractive;
	public boolean guideathactive;
	public boolean guigameoveractive;
	public boolean bgtransitionactive;
	
	public static boolean ambientwindplaying;
	
	public UnicodeFont guifont1x;
	public UnicodeFont guifont2x;
	public UnicodeFont guifontmm2x;
	public UnicodeFont guifontmm1x;
	
	public Color guicolor;
	public Color guibarcolor;
	
	public EntityPowerup lastpowerup;
	
	public enum GameState {mainmenu, ingame, paused, death, gameover, awaitinginput, quitting};
	public GameState state;
	public GameState prevstate;
	
	public GuiButton hoverbutton;
	public GuiButton selecteddiff;
	
	public Image guibar;
	public Image normaltile;
	public Image icetile;
	public Image mudtile;
	public Image crumblytile;
	public Image rotatestile;
	public Image rotatesfull;
	public Image slidestile;
	public Image playerimage;
	public Image curbg;
	public Image bg1;
	public Image bg2;
	public Image bg3;
	public Image bg4;
	public Image bg5;
	public Image bg6;
	public Image bg7;
	public Image deathwindow;
	public Image gameoverwindow;
	public Image mainmenu;
	public Image obstacleboulder;
	public Image collectablestar;
	public Image collectableflip;
	public Image collectableslow;
	public Image collectablefast;
	public Image collectabletrpl;
	public Image collectablelife;
	public Image menucloud1;
	public Image menucloud2;
	
	public static Sound jump;
	public static Sound applause;
	public static Sound phasein;
	public static Sound phaseout;
	public static Sound rotate;
	public static Sound death;
	public static Sound ambientwind1;
	public static Sound music;
	public static Sound pickup;
	public static Sound countdown;
	
	public Map<Integer, Image> leveltobg;
	
	public static final String guilevelup = "LEVEL UP!";
	public static final String guiscore = "Score: ";
	public static final String guihighscore = "High Score: ";
	public static final String guipause1 = "Game is paused";
	public static final String guipause2 = "press p to resume";
	public static final String guilevel = "Level: ";
	public static final String guimmtitle = "PLATFORMER";
	public static final String guimmstart = "Start";
	public static final String guimmquit = "Quit";
	public static final String guimmd1 = "Easy";
	public static final String guimmd2 = "Normal";
	public static final String guimmd3 = "Difficult";
	public static final String guimmd4 = "Expert";
	public static final String guideath1 = "Oh no!";
	public static final String guideath2 = "press R to resume";
	public static final String guigameover1 = "Game over!";
	public static final String guigameover2 = "press R to try again with the same difficulty";
	public static final String guigameover3 = "press ESC to return to the main menu";
	public static final String guimmcontrols = "AD or Arrow Keys to move";
	public static final String guimmcontrols2 = "Space to jump";
	

}
